package task4;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicReference;

public class Sequence {
	
	private final AtomicReference<BigInteger> valueHolder  =
			new AtomicReference<BigInteger>();
	
	private BigInteger multiplier = new BigInteger("2");
	
	public Sequence(BigInteger value){
		valueHolder.set(value);	
	}
	
	public BigInteger next(){
		while(true){
			BigInteger current = valueHolder.get();
			BigInteger next = current.multiply(multiplier);
			if(valueHolder.compareAndSet(current, next)){
				return next;
			}
		}
	}
	
	public static void main(String[] str)
			throws InterruptedException, ExecutionException{
		
		Sequence sequence = new Sequence(new BigInteger("1"));
		int numberOfTheads = 10;
		int calls = 20;
		List<Future<List<BigInteger>>> results = new ArrayList<Future<List<BigInteger>>>();
		
		ExecutorService service = Executors.newCachedThreadPool();
		
		for(int i = 0; i < numberOfTheads; i++){
			results.add(service.submit(new User(sequence, calls)));
		}
		
		BigInteger current = new BigInteger("1");
		BigInteger multiplier = new BigInteger("2");
		Set<BigInteger> set = new HashSet<BigInteger>();
		for(int i = 0; i < numberOfTheads*calls; i++){
			current = current.multiply(multiplier);
			set.add(current);
		}
		
		for(Future<List<BigInteger>> futureList : results){
			List<BigInteger> list = futureList.get();
			for(BigInteger val : list){
				if(set.contains(val)){
					set.remove(val);
				} else {
					System.out.println("duplicate " + val);
				}
			}
		}
		
		if(!set.isEmpty()){
			System.out.println("some element loss");
		}
		
		service.shutdown();
	}
}

class User implements Callable<List<BigInteger>>{
	
	Sequence sequence;
	int calls;
	
	List<BigInteger> data = new ArrayList<BigInteger>();
	
	public User(Sequence sequence, int calls) {
		this.sequence = sequence;
		this.calls = calls;
	}

	public List<BigInteger> call(){
		for(int i = 0; i < calls; i++){
			BigInteger next = sequence.next();
			System.out.println(next);
			data.add(next);
		}
		return data;
	}
	
	
}